package com.jzo2o.orders.manager.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jzo2o.api.customer.AddressBookApi;
import com.jzo2o.api.customer.dto.response.AddressBookResDTO;
import com.jzo2o.api.foundations.ServeApi;
import com.jzo2o.api.foundations.dto.response.ServeAggregationResDTO;
import com.jzo2o.api.orders.dto.request.OrderCancelReqDTO;
import com.jzo2o.api.orders.dto.response.OrderResDTO;
import com.jzo2o.api.orders.dto.response.OrderSimpleResDTO;
import com.jzo2o.api.trade.NativePayApi;
import com.jzo2o.api.trade.TradingApi;
import com.jzo2o.api.trade.dto.request.NativePayReqDTO;
import com.jzo2o.api.trade.dto.response.NativePayResDTO;
import com.jzo2o.api.trade.dto.response.TradingResDTO;
import com.jzo2o.api.trade.enums.PayChannelEnum;
import com.jzo2o.api.trade.enums.TradingStateEnum;
import com.jzo2o.common.enums.EnableStatusEnum;
import com.jzo2o.common.expcetions.ForbiddenOperationException;
import com.jzo2o.common.utils.DateUtils;
import com.jzo2o.common.utils.ObjectUtils;
import com.jzo2o.common.utils.StringUtils;
import com.jzo2o.mvc.utils.UserContext;
import com.jzo2o.orders.base.constants.RedisConstants;
import com.jzo2o.orders.base.enums.OrderPayStatusEnum;
import com.jzo2o.orders.base.enums.OrderStatusEnum;
import com.jzo2o.orders.base.mapper.OrdersCanceledMapper;
import com.jzo2o.orders.base.mapper.OrdersMapper;
import com.jzo2o.orders.base.mapper.OrdersRefundMapper;
import com.jzo2o.orders.base.model.domain.Orders;
import com.jzo2o.orders.base.model.domain.OrdersCanceled;
import com.jzo2o.orders.base.model.domain.OrdersRefund;
import com.jzo2o.orders.manager.model.dto.OrderCancelDTO;
import com.jzo2o.orders.manager.model.dto.request.OrdersPayReqDTO;
import com.jzo2o.orders.manager.model.dto.request.PlaceOrderReqDTO;
import com.jzo2o.orders.manager.model.dto.response.OrdersPayResDTO;
import com.jzo2o.orders.manager.model.dto.response.PlaceOrderResDTO;
import com.jzo2o.orders.manager.porperties.TradeProperties;
import com.jzo2o.orders.manager.service.IOrdersManagerService;
import com.jzo2o.orders.manager.service.IOrdersRefundService;
import com.jzo2o.orders.manager.strategy.OrderCancelStrategyManager;
import com.jzo2o.redis.annotations.Lock;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.List;

import static com.jzo2o.orders.base.constants.FieldConstants.SORT_BY;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author itcast
 * @since 2023-07-10
 */
@Slf4j
@Service
public class OrdersManagerServiceImpl extends ServiceImpl<OrdersMapper, Orders> implements IOrdersManagerService {

    @Autowired
    private ServeApi serveApi;

    @Autowired
    private AddressBookApi addressBookApi;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private IOrdersManagerService owner;

    @Autowired
    private NativePayApi nativePayApi;

    @Autowired
    private TradeProperties tradeProperties;

    @Autowired
    private TradingApi tradingApi;

    @Autowired
    private OrdersCanceledMapper ordersCanceledMapper;

    @Autowired
    private OrdersRefundMapper ordersRefundMapper;

    @Autowired
    private OrderCancelStrategyManager orderCancelStrategyManager;

    @Override
    public List<Orders> batchQuery(List<Long> ids) {
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery().in(Orders::getId, ids).ge(Orders::getUserId, 0);
        return baseMapper.selectList(queryWrapper);
    }

    @Override
    public Orders queryById(Long id) {
        return baseMapper.selectById(id);
    }

    /**
     * 滚动分页查询
     *
     * @param currentUserId 当前用户id
     * @param ordersStatus  订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
     * @param sortBy        排序字段
     * @return 订单列表
     */
    @Override
    public List<OrderSimpleResDTO> consumerQueryList(Long currentUserId, Integer ordersStatus, Long sortBy) {
        //1.构件查询条件
        LambdaQueryWrapper<Orders> queryWrapper = Wrappers.<Orders>lambdaQuery()
                .eq(ObjectUtils.isNotNull(ordersStatus), Orders::getOrdersStatus, ordersStatus)
                .lt(ObjectUtils.isNotNull(sortBy), Orders::getSortBy, sortBy)
                .eq(Orders::getUserId, currentUserId)
                .eq(Orders::getDisplay, EnableStatusEnum.ENABLE.getStatus());
        Page<Orders> queryPage = new Page<>();
        queryPage.addOrder(OrderItem.desc(SORT_BY));
        queryPage.setSearchCount(false);

        //2.查询订单列表
        Page<Orders> ordersPage = baseMapper.selectPage(queryPage, queryWrapper);
        List<Orders> records = ordersPage.getRecords();
        List<OrderSimpleResDTO> orderSimpleResDTOS = BeanUtil.copyToList(records, OrderSimpleResDTO.class);
        return orderSimpleResDTOS;

    }
    /**
     * 根据订单id查询
     *
     * @param id 订单id
     * @return 订单详情
     */
    @Override
    public OrderResDTO getDetail(Long id) {
        Orders orders = queryById(id);
        OrderResDTO orderResDTO = BeanUtil.toBean(orders, OrderResDTO.class);
        return orderResDTO;
    }

    /**
     * 订单评价
     *
     * @param ordersId 订单id
     */
    @Override
    @Transactional
    public void evaluationOrder(Long ordersId) {
//        //查询订单详情
//        Orders orders = queryById(ordersId);
//
//        //构建订单快照
//        OrderSnapshotDTO orderSnapshotDTO = OrderSnapshotDTO.builder()
//                .evaluationTime(LocalDateTime.now())
//                .build();
//
//        //订单状态变更
//        orderStateMachine.changeStatus(orders.getUserId(), orders.getId().toString(), OrderStatusChangeEventEnum.EVALUATE, orderSnapshotDTO);
    }




    /**
     * 订单支付
     *
     * @param id            订单id
     * @param ordersPayReqDTO 支付参数
     * @return 支付结果
     */
    //todo 每日训练
    @Override
    public OrdersPayResDTO pay(Long id, OrdersPayReqDTO ordersPayReqDTO) {
        //1，先校验，订单是否存在，支付状态是否合格
        Orders order = this.getById(id);
        if(ObjectUtil.isNull( order)){
            throw new  ForbiddenOperationException("订单不存在");
        }
        if(order.getPayStatus()==4&& StringUtils.isNotEmpty(order.getTransactionId())){
            //支付订单号只有支付成功才有-order.getTransactionId()
            throw new  ForbiddenOperationException("订单已支付");
        }


        NativePayReqDTO nativePayReqDTO = new NativePayReqDTO();
        //2，准备参数
        nativePayReqDTO.setProductAppId("jzo2o.orders");
        nativePayReqDTO.setProductOrderNo(id);
        nativePayReqDTO.setTradingAmount(order.getRealPayAmount());
        nativePayReqDTO.setMemo(order.getServeItemName());//备注为支付项目名称

        //商户号和交易渠道的处理重点
          //2.1通过配置文件，微信使用微信商户号
        if(ObjectUtil.equals(PayChannelEnum.WECHAT_PAY,ordersPayReqDTO.getTradingChannel())){
            nativePayReqDTO.setEnterpriseId(tradeProperties.getWechatEnterpriseId());
        }
          //通过配置文件，支付宝使用支付宝商户号
        if(ObjectUtil.equals(PayChannelEnum.ALI_PAY,ordersPayReqDTO.getTradingChannel())){
            nativePayReqDTO.setEnterpriseId(tradeProperties.getAliEnterpriseId());
        }

        //2.2交易渠道判断
        if(StringUtils.equals(order.getTradingChannel(),ordersPayReqDTO.getTradingChannel().toString())
        &&StringUtils.isNotEmpty(order.getTradingChannel())){
            //判断订单支付渠道与传入的是否一致
             //一致,不改变
            nativePayReqDTO.setChangeChannel(false);
        }else {
            //不一致，改变
            nativePayReqDTO.setChangeChannel(true);
        }


        //3，调用支付微服务
        NativePayResDTO downLineTrading = nativePayApi.createDownLineTrading(nativePayReqDTO);

        //4，更新订单表数据(支付服务交易单号 支付渠道)
        order.setTradingOrderNo(downLineTrading.getTradingOrderNo());//支付服务交易单号
        order.setTradingChannel(downLineTrading.getTradingChannel());//支付渠道
        this.updateById(order);
        //5，处理返回参数
        OrdersPayResDTO ordersPayResDTO = BeanUtil.copyProperties(downLineTrading, OrdersPayResDTO.class);
        ordersPayResDTO.setPayStatus(2);//未支付
        return ordersPayResDTO;
    }

    //支付查询api
    //传入订单id
    //todo 每日训练
    @Override
    public OrdersPayResDTO payResult(Long id) {
        //1,校验订单是否存在，订单支付状态是否为待支付，且交易订单号不为空（查询后或者支付服务回调后才修改支付状态为已支付，此前一直是待支付）
        Orders orders = this.getById(id);
        if(ObjectUtil.isNull(orders)){
            throw new  ForbiddenOperationException("订单不存在");
        }
        //2，调用查询api（根据交易订单id查询）,并修改订单
        boolean isUpdate=true;
        if(orders.getPayStatus()==2&&ObjectUtil.isNotNull(orders.getTransactionId())){
            //此时说明以调用支付服务下单支付，需查询结果并修改订单字段
            TradingResDTO payResult = tradingApi.findTradResultByTradingOrderNo(orders.getTradingOrderNo());
            TradingStateEnum tradingState = payResult.getTradingState();
            //根据交易订单支付状态修改订单状态和支付状态字段
             isUpdate = this.lambdaUpdate()
                    //付款失败
                    .set(ObjectUtil.equals(tradingState, TradingStateEnum.FKSB), Orders::getOrdersStatus, OrderStatusEnum.CLOSED.getStatus())
                    //支付成功-修改两个字段
                    .set(ObjectUtil.equals(tradingState, TradingStateEnum.YJS), Orders::getOrdersStatus, OrderStatusEnum.DISPATCHING.getStatus())
                    .set(ObjectUtil.equals(tradingState, TradingStateEnum.YJS), Orders::getPayStatus, OrderPayStatusEnum.PAY_SUCCESS.getStatus())
                    //支付取消
                    .set(ObjectUtil.equals(tradingState, TradingStateEnum.QXDD), Orders::getOrdersStatus, OrderStatusEnum.CANCELED.getStatus())
                    .set(ObjectUtil.isNotEmpty(payResult.getTransactionId()), Orders::getTransactionId, payResult.getTransactionId())
                    .eq(Orders::getId, id)
                    .update();
        }
        if(!isUpdate){
            throw new  ForbiddenOperationException("订单更新失败");
        }
        //3，处理返回参数
        Orders newOrders = this.getById(id);
        OrdersPayResDTO ordersPayResDTO = new OrdersPayResDTO();
        ordersPayResDTO.setProductOrderNo(newOrders.getId());
        ordersPayResDTO.setPayStatus(newOrders.getPayStatus());
        ordersPayResDTO.setTradingOrderNo(newOrders.getTradingOrderNo());
        ordersPayResDTO.setTradingChannel(newOrders.getTradingChannel());

        return ordersPayResDTO;
    }


    //订单取消-使用策略类的方法
    @Override
    //使用这个实体类是因为包含了真实付款金额等信息，在退款操作中需要使用
    public void cancel(OrderCancelDTO orderCancelDTO) {
        //通过策略管理器来调用策略执行
        orderCancelStrategyManager.cancel(orderCancelDTO);
    }


    /*//订单取消-不使用策略类是的原代码
    //todo 每日训练
    @Override
    //使用这个实体类是因为包含了真实付款金额等信息，在退款操作中需要使用
    public void cancel(OrderCancelDTO orderCancelDTO) {
        //思路；根据订单状态判断采取不同策略，订单状态为待支付，订单状态为派单中
        //准备工作：涉及取消订单表和退款表，因此需要取消实体类同时含有处理两张表的字段
        Orders orders = this.getById(orderCancelDTO.getId());
        BeanUtil.copyProperties(orders, orderCancelDTO);
        //订单状态，0：待支付，100：派单中，200：待服务，300：服务中，400：待评价，500：订单完成，600：已取消，700：已关闭
        if(ObjectUtil.equals(orders.getOrdersStatus(),0)){
         //1，判断订单状态为待支付-更改订单状态，记录取消记录
            //todo 重点用owner才能保证两个方法的事务
            owner.cancelByNoPay(orderCancelDTO);
        }

        else if(ObjectUtil.equals(orders.getOrdersStatus(),100)){
         //2，判断订单状态为派送中-更改订单状态，记录取消记录，调用退款服务
            owner.cancelByDispatching(orderCancelDTO);
        }
        else if(true){
            throw new  ForbiddenOperationException("其余状态");
        }

    }

    //取消派单中订单
    @Transactional(rollbackFor = Exception.class)
    public void cancelByDispatching(OrderCancelDTO orderCancelDTO) {
        //1,更新订单表
        boolean isUpdate = this.lambdaUpdate()
                .eq(Orders::getId, orderCancelDTO.getId())
                .set(Orders::getOrdersStatus, OrderStatusEnum.CLOSED.getStatus())
                .update();
        if(!isUpdate){
            throw new  ForbiddenOperationException("订单更新失败");
        }
        //2，保存订单取消表
        OrdersCanceled ordersCanceled = new OrdersCanceled();
        ordersCanceled.setId(orderCancelDTO.getId());
        ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
        ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
        ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
        ordersCanceled.setCancelReason(orderCancelDTO.getCancelReason());
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledMapper.insert(ordersCanceled);
        //3，调用退款服务
        OrdersRefund ordersRefund = BeanUtil.copyProperties(orderCancelDTO, OrdersRefund.class);
        ordersRefundMapper.insert(ordersRefund);
    }

    //取消待支付订单: 1) 更新订单状态为已取消  2) 保存取消订单记录
    @Transactional(rollbackFor = Exception.class)
    public void cancelByNoPay(OrderCancelDTO orderCancelDTO) {
        //1，更新订单表
        boolean isUpdate = this.lambdaUpdate()
                .eq(Orders::getId, orderCancelDTO.getId())
                .set(Orders::getOrdersStatus, OrderStatusEnum.CANCELED.getStatus())
                .update();
        if(!isUpdate){
            throw new  ForbiddenOperationException("订单更新失败");
        }
        //2，保存取消表
        OrdersCanceled ordersCanceled = new OrdersCanceled();
        ordersCanceled.setId(orderCancelDTO.getId());
        ordersCanceled.setCancellerId(orderCancelDTO.getCurrentUserId());
        ordersCanceled.setCancelerName(orderCancelDTO.getCurrentUserName());
        ordersCanceled.setCancellerType(orderCancelDTO.getCurrentUserType());
        ordersCanceled.setCancelReason(orderCancelDTO.getCancelReason());
        ordersCanceled.setCancelTime(LocalDateTime.now());
        ordersCanceledMapper.insert(ordersCanceled);
    }*/




}
